﻿#include "com.h"

#include <DbgHelp.h>
#include <QCoreApplication>
#include <QDir>
#include <QThread>
#include <QTimer>
#include <QTranslator>
#include <QSettings>
#include <QSharedMemory>
#include <QDebug>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QEventLoop>
#include <QUrl>
#include <string.h>
#include "simpleLog.h"

// 从1601年1月1日0:0:0:000到1970年1月1日0:0:0:000的时间(单位s)
#define EPOCHFILETIME   (11644473600UL)
uint64_t __eufy_get_timestamp()
{
#if defined(_WIN32)
    FILETIME ft;
    GetSystemTimeAsFileTime(&ft);  // 1.6ns, 速度比Qt API快多了
    // GetSystemTimePreciseAsFileTime(&ft);  // 17.5ns, 速度比Qt API快多了
    ULARGE_INTEGER uli;
    uli.LowPart = ft.dwLowDateTime;
    uli.HighPart = ft.dwHighDateTime;
    return uli.QuadPart;
#elif defined(__linux)
    struct timespec t;
    clock_gettime(CLOCK_MONOTONIC, &t);
    return t.tv_sec * 10000000 + t.tv_nsec / 100;
#else
    return QDateTime::currentDateTime().toMSecsSinceEpoch() * 10000;  // 920ns
#endif
}

namespace com
{

double now()
{
    static constexpr double S_2_100NS_DBL = S_2_100NS;
    const auto t = __eufy_get_timestamp();
    return t / S_2_100NS - EPOCHFILETIME + t % S_2_100NS / S_2_100NS_DBL;
}

void qtGuiWait(unsigned long ms, bool isResponseUser)
{
    QCoreApplication::processEvents(isResponseUser ? QEventLoop::AllEvents : QEventLoop::ExcludeUserInputEvents);
    QThread::msleep(ms);
}

void delay(int ms)
{
    QEventLoop eventloop;
    QTimer::singleShot(ms, &eventloop, SLOT(quit()));
    eventloop.exec();
}

void busyWait(double ms)
{
    const auto t0 = __eufy_get_timestamp();
    const uint64_t ns100 = ms * 10000;
    while (__eufy_get_timestamp() - t0 < ns100);
}

int split(char * src, const char * separator, char ** dest, int destLen)
{
    if ((src == 0) || (strlen(src) <= 0) || (separator == 0) || (strlen(separator) <= 0) || (dest == 0) || (destLen <= 0)) { return -1; }

    int size;
    size_t i, length = strlen(separator);
    dest[0] = src;

    for (size = 1; *src; ++src)
    {
        for (i = 0; i < length; ++i)
        {
            if (*src == separator[i])
            {
                *src = 0;

                if (size >= destLen) { return size; }

                dest[size++] = src + 1;
                break;
            }
        }
    }

    if (size < destLen)
    {
        dest[size] = 0;  //此处直接赋值有bug，需加上 if(size < destLen) 判断才能赋值
    }

    return size;
}

bool clearDir(const QString & path)
{
    if (path.isEmpty()) { return false; }

    QDir dir(path);
    if (!dir.exists()) { return true; }
    dir.setFilter(QDir::AllEntries | QDir::Hidden | QDir::System | QDir::NoDotAndDotDot);  //设置过滤
    const QFileInfoList & fileList = dir.entryInfoList();                                  // 获取所有的文件信息
    foreach (QFileInfo file, fileList)
    {  //遍历文件信息
        if (file.isDir())
        {  // 递归删除
            if (!deleteDir(file.absoluteFilePath())) { qWarning() << "deleteDir error!!" << file.absoluteFilePath(); }
        }
        else
        {  // 是文件，删除
            if (!file.dir().remove(file.fileName())) { qWarning() << "removeFile error!!" << file.fileName(); }
        }
    }

    return true;  // 删除文件夹
}

bool deleteDir(const QString & path)
{
    if (!clearDir(path)) { qWarning() << "clearDir error!!" << path; }
    QDir dir;
    return dir.rmdir(path);  // 删除文件夹
}

bool checkCreateDir(const QString & path)
{
    QDir dir;
    if (!dir.exists(path)) { return dir.mkpath(path); }

    return true;
}

bool createEmptyDir(const QString & path)
{
    if (!clearDir(path)) { return false; }
    return checkCreateDir(path);
}

double average(const double * p, int len)
{
    int count2 = 0;
    double sum = 0.0;
    for (int i = 0; i < len; ++i)
    {
        if (p[i] != 0)
        {
            sum += p[i];
            ++count2;
        }
    }

    return (0 == count2) ? 0 : sum / count2;
}

// double precision(double d, int n, bool isRounding)
// {
//     const double times = pow(10, n);
//     const double round = isRounding ? 0.5 : 0;
//     return floor(d * times + round) / times;
// }

bool loadTranslateFile(const QString & fileName)
{
    QTranslator trans;
    return trans.load(fileName) && QCoreApplication::installTranslator(&trans);
}

static constexpr const char * AUTO_RUN = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run";
void setProcessAutoRun(const QString & appPath, bool enable)
{
    QSettings settings(AUTO_RUN, QSettings::NativeFormat);

    //以程序名称作为注册表中的键,根据键获取对应的值（程序路径）
    const QFileInfo fileInfo(appPath);
    const QString & name = fileInfo.baseName();  //键-名称

    //如果注册表中的路径和当前程序路径不一样，则表示没有设置自启动或本自启动程序已经更换了路径
    const QString & oldPath = settings.value(name).toString();    //获取目前的值-绝对路劲
    const QString & newPath = QDir::toNativeSeparators(appPath);  // toNativeSeparators函数将"/"替换为"\"
    if (enable)
    {
        if (oldPath != newPath) { settings.setValue(name, newPath); }
    }
    else
    {
        settings.remove(name);
    }
}

void setProcessAutoRun(bool enable) { setProcessAutoRun(QCoreApplication::applicationFilePath(), enable); }

bool checkAppIsRunning()
{
    static QSharedMemory shareMem(QCoreApplication::applicationName());  //创建共享内存块
    return !shareMem.create(1);
}

bool setProcessPriority(int prio) { return 0 != SetPriorityClass(GetCurrentProcess(), prio); }
bool setThreadPriority(int prio) { return 0 != SetThreadPriority(GetCurrentThread(), prio); }
bool setThreadAffinity(int cpu) { return 0 != SetThreadAffinityMask(GetCurrentThread(), 1 << cpu); }

static LONG ExceptionCapture(EXCEPTION_POINTERS * pException)
{
    QLOGW("************************** GAME FAILED **************************");
    CHECK_PTR(pException);
    WCHAR fileName[MAX_PATH] = {0};
    SYSTEMTIME t;
    GetLocalTime(&t);
    swprintf_s(fileName, sizeof(fileName), L"log/%4d-%02d-%02dT%02d-%02d-%02d.dmp", t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond);

    //创建 Dump 文件
    HANDLE hDumpFile = CreateFile(fileName, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
    if( hDumpFile != INVALID_HANDLE_VALUE)
    {
        MINIDUMP_EXCEPTION_INFORMATION dumpInfo;
        dumpInfo.ExceptionPointers = pException;
        dumpInfo.ThreadId = GetCurrentThreadId();
        dumpInfo.ClientPointers = TRUE;
        //写入Dump文件内容
        if (!MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpNormal, &dumpInfo, nullptr, nullptr))
        {
            DWORD dwErrorCode = GetLastError();
            LPVOID lpMsgBuf;
            FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                          NULL, dwErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                          (LPTSTR)&lpMsgBuf, 0, NULL);
            qCritical() << "Error: MiniDumpWriteDump failed" << dwErrorCode << QString::fromWCharArray((LPCTSTR)lpMsgBuf);
            LocalFree(lpMsgBuf);
        }
        if (!CloseHandle(hDumpFile)) { HERE_ERROR; }
        qCritical() << "程序崩了, 创建Dump文件" << QString::fromWCharArray(fileName);
    } else { HERE_ERROR; }
    CQtLog::close();
    return EXCEPTION_EXECUTE_HANDLER;
}

void SetExceptionFilter() { SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)ExceptionCapture); }

bool checkNetConnect(const char * url)
{
    QNetworkAccessManager manager;
    QNetworkRequest request((QUrl(url)));

    // 发送 GET 请求
    QNetworkReply * reply = manager.get(request);

    // 创建一个事件循环来等待请求完成
    QEventLoop loop;
    QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
    loop.exec();

    // 检查请求结果
    bool isSuccess = false;
    if (reply->error() == QNetworkReply::NoError)
    {
        QLOGIV2("成功访问:", url);
        isSuccess = true;
    }
    else { qWarning() << "无法访问:" << url << ", 错误:" << reply->errorString(); }

    reply->deleteLater();
    return isSuccess;
}

int Frequency::update()
{
    ++count_;
    const auto t1 = __eufy_get_timestamp();
    if (t1 - t0_ > 1000 * 10000)  // 1s计算一次
    {
        fps_ = count_;
        count_ = 0;
        t0_ = t1;
    }
    return fps_;
}

const char * QString2_c_str(const QString & s, char * str, int size)
{
    const int length = s.size();
    if (length >= size) { return nullptr; }
    auto const data = s.utf16();

    // 遍历每个 QChar，并提取低字节
    for (int i = 0; i < length; ++i) { str[i] = data[i]; }
    str[length] = '\0';

    return str;
}

}  // namespace com
